/// @file S.cc
/// @brief Classes and methods in support of the color handling classes SMap
///        and S

// *************************************************************************

// Copyright© 2010 Texas A&M University, 
//                 College Station, Texas
//                 Contact: R Crosby <rwc@cs.tamu.edu>
//
// Copyright© 2003, 2004 by BiRC -- Bioinformatics Research Center
//                 University of Aarhus, Denmark
//                 Contact: Thomas Mailund <mailund@birc.dk>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// *************************************************************************

#include <cassert>

using namespace std;

#include "options.hh"
#include "S.hh"

namespace {

    // Ansi color codes used in debugging output (with -a option)
    const char* const green = "\033[;32m";
    const char* const red   = "\033[;31m";
    const char* const blue  = "\033[;34m";
    const char* const none  = "\033[;35m";
    const char* const reset = "\033[0m";

};

// *****************************************************************************
/// @brief Constructor for an s node
///
/// Gets a reference to a color code in the SMap vector to use as the color
///
/// @param name Leaf node name
/// @param c Color code to initially set
/// @param parent Reference to owning SMap

S::S(const string   name, 
     const colour_t c,
     SMap &         parent) 
    : _name(name), _c(parent.SetC(c)), _parent(parent)
{
    _parent.Inc(c);
}

// *****************************************************************************
/// @brief Increment overall color count
///
/// @param c Color code to increment

inline void 
SMap::Inc(const S::colour_t c)
{
    switch(c) {
    case S::A: ++_As; break;
    case S::B: ++_Bs; break;
    case S::C: ++_Cs; break;
    case S::None:     break;
    default: 
	assert(0);	    
    }
}

// *****************************************************************************
/// @brief Decrement overall color count
///
/// @param c Color code to increment

inline void 
SMap::Dec(const S::colour_t c)
{
    switch(c) {
    case S::A: --_As; break;
    case S::B: --_Bs; break;
    case S::C: --_Cs; break;
    case S::None:     break;
    default:
	assert(0);	    
    }
}

// *****************************************************************************
/// @brief Set color of a leaf
///
/// @param c Color code to set

void 
S::Colour(const colour_t c) const
{
    _parent.Dec(_parent[_c]);
    _parent.Inc(c);
    _parent[_c] = c;
}

// *****************************************************************************
/// @brief Return color associated with node
///

const S::colour_t
S::Colour() const
{
    return _parent[_c]; 
}

// *****************************************************************************
/// @brief Clean up allocated S nodes

SMap::~SMap()
{

    map<string, S*>::iterator i = _map.begin();

    for ( ; i != _map.end(); i++ ) {
	delete (*i).second;
    }

}

// *****************************************************************************
/// @brief Return overall number of nodes with specified color
///
/// @param c Color code to query

const unsigned 
SMap::Numberof(const S::colour_t c)
{

    switch(c) {
    case S::A: return _As;
    case S::B: return _Bs;
    case S::C: return _Cs;
    default:   assert(0);			  // shouldn't happen
    }

    return 0;					  // to avoid warnings

}

// *****************************************************************************
/// @brief Factory to create S nodes
///
/// @param n Name of node to create

S *
SMap::Create(const char * n) throw(AllReadyCreated)
{

    string name(n);

    map<string, S*>::iterator itr = _map.find(name);
    if (itr != _map.end()) return itr->second;

    if (_map.size() == 0) _root_label = name;

    S *s = new S(name, S::A, *this);
    _map[name] = s;
    return s;

}

// *****************************************************************************
/// @brief Find the S node for a named leaf
///
/// @param name Leaf to find

S *
SMap::Lookup(const string name) throw(NotCreated)
{
    map<string, S*>::iterator itr = _map.find(name);
    if (itr == _map.end()) throw NotCreated(name);
    return itr->second;
}

// *****************************************************************************
/// @brief Reset the map to empty...
///

void
SMap::ResetAll()
{
    
    _map.clear();
    _As = _Bs = _Cs = 0;
}

// *****************************************************************************
/// @brief Set entire map to a color
///

void
SMap::ColourAll(const S::colour_t c)
{

    map<string, S*>::iterator i;
    for (i = _map.begin(); i != _map.end(); ++i)
	i->second->Colour(c);
}

// *****************************************************************************
/// @brief Output an S node
///

ostream &
operator<<(ostream &os, const S &s)
{


    if (options::ansiColor) os << ( s.Colour() == S::A ? green : 
     	                          ( s.Colour() == S::B ? red : 
				  ( s.Colour() == S::C ? blue : none ) ) );
    os << "S[\"" << s.Name() << "\", " << s.Colour() << "]";
    if (options::ansiColor) os << reset;
    return os;
}

// *****************************************************************************
/// @brief Formatted print of the mapping table
///

const void
SMap::PrintTable ( ostream &os ) 
{

    os << "S::Table[";

    for ( map<string, S*>::iterator i = _map.begin(); i != _map.end(); ++i ) {
	const S& s  = *(i->second);
	if (options::ansiColor) os << ( s.Colour() == S::A ? green : 
				      ( s.Colour() == S::B ? red : 
				      ( s.Colour() == S::C ? blue : none ) ) );
	os << s.Name() << "/" << s.Colour() << " ";
	if (options::ansiColor) os << reset;
    }
    
    os << "]";

}

// *****************************************************************************
/// @brief Dump the character vector
///

const void
SMap::DumpTable ( ostream &os ) 
{

    for ( vector<S::colour_t>::iterator i = _colors.begin(); i != _colors.end(); ++i ) 	
	os << *i;

}
